package com.ruoyi.app.business.project;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.app.business.account.CompanyAccountBusiness;
import com.ruoyi.app.business.account.LaborAccountBusiness;
import com.ruoyi.app.config.platform.PlatFormConfig;
import com.ruoyi.app.config.util.UserUtil;
import com.ruoyi.app.converter.ProjectSettleConverter;
import com.ruoyi.app.domain.project.settle.LaborSalary;
import com.ruoyi.app.domain.project.settle.ProjectSettleDetail;
import com.ruoyi.app.domain.project.settle.ProjectSettleDetailItem;
import com.ruoyi.app.domain.project.settle.ProjectSettleInfo;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.CommonUtils;
import com.ruoyi.common.core.utils.PageUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.page.CommonPageInfo;
import com.ruoyi.service.company.entity.CompanyEntity;
import com.ruoyi.service.company.service.CompanyService;
import com.ruoyi.service.project.entity.ProjectDeliveryEntity;
import com.ruoyi.service.project.entity.ProjectEntity;
import com.ruoyi.service.project.entity.ProjectLaborEntity;
import com.ruoyi.service.project.entity.ProjectSettleDetailEntity;
import com.ruoyi.service.project.entity.ProjectSettleEntity;
import com.ruoyi.service.project.enums.SettleStatusEnum;
import com.ruoyi.service.project.enums.SettleTypeEnum;
import com.ruoyi.service.project.model.PostInfo;
import com.ruoyi.service.project.query.SettleQuery;
import com.ruoyi.service.project.service.ProjectSettleDetailService;
import com.ruoyi.service.project.service.ProjectSettleService;
import com.ruoyi.service.user.entity.UserEntity;
import com.ruoyi.service.user.service.UserService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * <p>
 * 作用：项目结算单
 * </p>
 *
 * @author Zane
 * @since 2025-10-26 4:03
 */
@Slf4j
@Component
public class ProjectSettleBusiness {

  @Resource
  private ProjectSettleService projectSettleService;
  @Resource
  private ProjectSettleDetailService projectSettleDetailService;
  @Resource
  private ProjectLaborBusiness projectLaborBusiness;
  @Resource
  private PlatFormConfig platFormConfig;
  @Resource
  private CompanyAccountBusiness companyAccountBusiness;
  @Resource
  private LaborAccountBusiness laborAccountBusiness;
  @Resource
  private TransactionTemplate transactionTemplate;
  @Autowired
  private CompanyService companyService;
  @Autowired
  private UserService userService;

  @Transactional(rollbackFor = Exception.class)
  public void generateSettlement(ProjectEntity project, ProjectDeliveryEntity delivery) {
    //初始化项目结算单
    final List<ProjectLaborEntity> laborList = projectLaborBusiness.listHiredByProjectId(
        project.getId());
    final BigDecimal laborCostPay = calculateLaborCost(project, laborList);
    final ProjectSettleEntity settle = initSettle(project, delivery, laborCostPay)
        .setLaborCount(laborList.size());
    final Map<String, BigDecimal> salaryOneDayMap = project.getPostsInfoList().stream()
        .collect(Collectors.toMap(PostInfo::buildCompareKey, PostInfo::getSalaryByDay));
    //生成明细
    List<ProjectSettleDetailEntity> settleDetailList = new ArrayList<>();
    // 平台抽点
    settleDetailList.add(buildSettleDetail(settle, SettleTypeEnum.DEDUCTION, settle.getCommission(),
        platFormConfig.getId(), SettleTypeEnum.DEDUCTION.getDesc(), platFormConfig.getPhone(), "",
        "", null))
    ;
    // 备用金使用
    settleDetailList.add(
        buildSettleDetail(settle, SettleTypeEnum.RESERVE_FUND, settle.getReserveFundUsed(),
            platFormConfig.getId(), SettleTypeEnum.RESERVE_FUND.getDesc(), "", "", "", null));
    // 员工薪资
    laborList.forEach(labor -> {
      settleDetailList.add(buildSettleLaborDetail(settle, labor, salaryOneDayMap));
    });
    projectSettleService.save(settle);
    projectSettleDetailService.saveBatch(settleDetailList);
  }

  private ProjectSettleDetailEntity buildSettleLaborDetail(ProjectSettleEntity settle,
      ProjectLaborEntity labor, Map<String, BigDecimal> salaryOneDayMap) {
    final String key = labor.postAndLevelKey();
    final BigDecimal salaryOneDay = salaryOneDayMap.get(key);
    final BigDecimal salary = salaryOneDay.multiply(BigDecimal.valueOf(labor.getRealDays()))
        .setScale(2, RoundingMode.HALF_UP);
    //岗位 级别 天数 薪资每天
    final String info = StringUtils.format("{} {} {}天 {}薪资/天", labor.getPostName(),
        labor.getPostLevelName(), labor.getRealDays(), salaryOneDay);
    //构建员工薪资对象
    final LaborSalary laborSalary = new LaborSalary()
        .setLaborId(labor.getLaborId())
        .setPostName(labor.getPostName())
        .setWorkDays(labor.getRealDays())
        .setSalaryOneDay(salaryOneDay)
        .setWorkDateBegin(labor.getHiredTime())
        .setWorkDateEnd(labor.getHiredTime().plusDays(labor.getRealDays()));
    //需要计算员工薪资
    return buildSettleDetail(settle, SettleTypeEnum.SALARY, salary, labor.getLaborId(),
        labor.getLaborName(), labor.getLaborPhone(), labor.getPostName(), info,
        JSONUtil.toJsonStr(laborSalary));
  }


  private ProjectSettleDetailEntity buildSettleDetail(ProjectSettleEntity settle,
      SettleTypeEnum settleType,
      BigDecimal settleAmount, Long targetId, String targetName, String phone, String postName,
      String info, String infoDataJson
  ) {
    return new ProjectSettleDetailEntity()
        .setProjectId(settle.getProjectId())
        .setProjectName(settle.getProjectName())
        .setPostName(postName)
        .setSettleType(settleType.getCode())
        .setSettleAmount(settleAmount)
        .setTargetId(targetId)
        .setTargetName(targetName)
        .setTargetPhone(phone)
        .setTargetInfo(info)
        .setInfoData(infoDataJson)
        .setCreateBy(settle.getCreateBy())
        .setCreateTime(settle.getCreateTime())
        .setUpdateBy(settle.getUpdateBy())
        .setUpdateTime(settle.getUpdateTime());

  }


  private ProjectSettleEntity initSettle(ProjectEntity project, ProjectDeliveryEntity delivery,
      BigDecimal laborCostPay) {
    final Long userId = UserUtil.getUserId();
    final LocalDateTime now = LocalDateTime.now();
    final BigDecimal laborCostPre = project.getPostsInfoList().stream()
        .map(PostInfo::getSalaryTotal)
        .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
    final BigDecimal payment = delivery.getReserveFundUsedAmount().add(laborCostPay);
    final BigDecimal commission = platFormConfig.calculateCommission(payment);
    final BigDecimal actualDeduction = platFormConfig.calculateAmountContainCommission(payment);
    //初始化项目结算单
    return new ProjectSettleEntity()
        .setComId(project.getComId())
        .setProjectId(project.getId())
        .setProjectName(project.getProjectName())
        .setSettleStatus(SettleStatusEnum.settling.getCode())
        .setProjectBudget(project.getProjectBudget())
        .setReserveFund(project.getReserveFund())
        .setLaborCost(laborCostPre)
        .setLockedAmount(project.getLockedAmount())
        .setReserveFundUsed(delivery.getReserveFundUsedAmount())
        .setLaborCostPay(laborCostPay)
        .setCommission(commission)
        .setCommissionRate(String.valueOf(platFormConfig.getCommissionRate()))
        .setActualDeduction(actualDeduction)
        .setProjectLeader(project.getProjectLeader())
        .setProjectManager(project.getCreateBy())
        .setCreateBy(userId)
        .setCreateTime(now)
        .setUpdateBy(userId)
        .setUpdateTime(now);
  }

  private BigDecimal calculateLaborCost(ProjectEntity project, List<ProjectLaborEntity> laborList) {
    final Map<String, BigDecimal> salaryOneDayMap = project.getPostsInfoList().stream()
        .collect(Collectors.toMap(PostInfo::buildCompareKey, PostInfo::getSalaryByDay));
    List<BigDecimal> costList = new ArrayList<>();
    laborList.stream()
        .collect(Collectors.groupingBy(ProjectLaborEntity::postAndLevelKey))
        .forEach((k, v) -> {
          final BigDecimal salaryOneDay = CommonUtils.requireNotNull(salaryOneDayMap.get(k),
              "员工岗位信息有误,请联系管理员");
          final Integer sumDays = v.stream().map(ProjectLaborEntity::getRealDays)
              .reduce(Integer::sum)
              .orElse(0);
          if (salaryOneDay.compareTo(BigDecimal.ZERO) == 0 || sumDays == 0) {
            throw new ServiceException("员工岗位信息有误,请联系管理员");
          }
          costList.add(salaryOneDay.multiply(BigDecimal.valueOf(sumDays)));
        });
    if (CollUtil.isNotEmpty(costList)) {
      return costList.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
    }
    throw new RuntimeException("员工岗位信息有误,请联系管理员");
  }


  public void settle() {
    final List<ProjectSettleEntity> needSettleList = projectSettleService.getNeedSettleList();
    if (CollUtil.isNotEmpty(needSettleList)) {
      final List<String> projectIds = needSettleList.stream().map(ProjectSettleEntity::getProjectId)
          .collect(Collectors.toList());
      final Map<String, List<ProjectSettleDetailEntity>> detailListMap =
          projectSettleDetailService.groupMapByProjectIds(projectIds);
      needSettleList.forEach(settle -> {
        //结算计算单
        try {
          if (!detailListMap.containsKey(settle.getProjectId())) {
            throw new RuntimeException("结算单有误[缺少结算明细],请联系管理员");
          }
          transactionTemplate.executeWithoutResult(status -> {
            // 结算公司
            companyAccountBusiness.settle(settle);
            // 结算员工
            laborAccountBusiness.settle(settle, detailListMap.get(settle.getProjectId()));
            settle.setSettleStatus(SettleStatusEnum.settled.getCode())
                .setUpdateBy(platFormConfig.getId())
                .setUpdateTime(LocalDateTime.now());
            projectSettleService.updateById(settle);
          });
        } catch (Exception e) {
          log.error(StringUtils.format("结算失败[{}]", settle.getProjectId()), e);
        }
      });
    }
  }

  public BigDecimal sumSettling() {
    return projectSettleDetailService.sumSettling(UserUtil.getUserId());
  }

  public CommonPageInfo<ProjectSettleInfo> demanderListSettle(SettleQuery settleQuery) {
    final CommonPageInfo<ProjectSettleEntity> pageInfo = PageUtils.startPage(settleQuery);
    pageInfo.initData(projectSettleService.demanderListSettle(settleQuery, UserUtil.getUserId()));
    return convert(pageInfo);
  }

  public CommonPageInfo<ProjectSettleInfo> platformListSettle(SettleQuery settleQuery) {
    final CommonPageInfo<ProjectSettleEntity> pageInfo = PageUtils.startPage(settleQuery);
    pageInfo.initData(projectSettleService.platformListSettle(settleQuery));
    return convert(pageInfo);
  }


  private CommonPageInfo<ProjectSettleInfo> convert(CommonPageInfo<ProjectSettleEntity> pageInfo) {
    final CommonPageInfo<ProjectSettleInfo> result = pageInfo.convert(
        ProjectSettleConverter.INSTANCE::toSettleInfo);
    if (CollUtil.isNotEmpty(pageInfo.getList())) {
      //补充公司名称
      fillCompanyInfo(result.getList());
      //补充项目负责人信息
      fillProjectLeaderInfo(result.getList());
    }
    return result;
  }

  private void fillProjectLeaderInfo(List<ProjectSettleInfo> settleList) {
    final List<Long> userIds = settleList.stream().map(ProjectSettleInfo::getProjectLeader)
        .collect(Collectors.toList());
    final Map<Long, UserEntity> userMap = userService.userMapByIds(userIds);
    settleList.forEach(v -> {
      final UserEntity userEntity = userMap.get(v.getProjectLeader());
      if (Objects.nonNull(userEntity)) {
        v.setProjectLeaderName(userEntity.getName());
        v.setProjectLeaderPhone(userEntity.getPhone());
      }
    });

  }

  private void fillCompanyInfo(List<ProjectSettleInfo> settleList) {
    final List<Long> comIds = settleList.stream().map(ProjectSettleInfo::getComId)
        .collect(Collectors.toList());
    final Map<Long, CompanyEntity> comMap = companyService.getMapByIds(comIds);
    settleList.forEach(v -> {
      final CompanyEntity companyEntity = comMap.get(v.getProjectLeader());
      if (Objects.nonNull(companyEntity)) {
        v.setComName(companyEntity.getComName());
      }
    });
  }


  public ProjectSettleDetail detailById(Long id) {
    final ProjectSettleEntity settle = CommonUtils.requireNotNull(projectSettleService.getById(id),
        "结算单ID不合法");
    ProjectSettleDetail detail = new ProjectSettleDetail()
        .setId(id)
        .setComId(settle.getComId())
        .setComName(companyService.getNameById(settle.getComId()));
    //查询结算明细
    final List<ProjectSettleDetailEntity> detailList = projectSettleDetailService.listByProjectId(
        settle.getProjectId());
    if (CollUtil.isNotEmpty(detailList)) {
      final List<ProjectSettleDetailItem> items = detailList.stream()
          .map(ProjectSettleConverter.INSTANCE::toSettleDetail).collect(Collectors.toList());
      final Map<Long, ProjectSettleDetailEntity> detailMap = detailList.stream()
          .collect(Collectors.toMap(ProjectSettleDetailEntity::getId, v -> v, (v1, v2) -> v1));
      items.forEach(v -> {
        final ProjectSettleDetailEntity detailEntity = CommonUtils.requireNotNull(
            detailMap.get(v.getId()), "数据出错,请联系管理员");
        if (StringUtils.isNotBlank(detailEntity.getInfoData())) {
          v.setLaborSalary(JSONUtil.toBean(detailEntity.getInfoData(), LaborSalary.class));
        }
      });
      detail.setItems(items);
    }
    return detail;
  }
}
